package com.mytx.live.scheduled;

import com.alibaba.fastjson.JSONObject;
import com.mytx.common.constant.Constant;
import com.mytx.common.utils.DateUtils;
import com.mytx.common.utils.EntityUtils;
import com.mytx.common.utils.IDUtils;
import com.mytx.mapper.*;
import com.mytx.pojo.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Component
public class ScheduledTask {
    Logger logger = LoggerFactory.getLogger(ScheduledTask.class);
    private static final long QUERY_COUNT = 100;
    @Autowired
    TopicMapper topicMapper;
    @Autowired
    private RedisTemplate redisTemplate;


    @Autowired
    UserMapper userMapper;
    @Autowired
    AdvertisementMapper advertisementMapper;

    @Autowired
    OrdersMapper ordersMapper;

    @Autowired
    DonationMapper donationMapper;

    @Autowired
    LiveRoomMapper liveRoomMapper;

    @Autowired
    DataSourceTransactionManager transactionManager;

    @Value(("${donation_threshold}"))
    double donation_threshold;//自动生成捐赠的阈值：超过这个值才生成捐赠

    @Value("${donation_rate}")//根据广告位投放金额自动生成捐赠的比率
            double donationRate;
    //广告位默认底价
    @Value("${advertisement_default_price}")
    BigDecimal advertisementDefaultPrice;

    /**
     * 每天10点执行结算 并生成新advertisement，关联到直播间，
     * 只能通过进入直播间侧滑出来
     */
    //  @Scheduled(fixedRate = 1000 * 60 * 3)//每个3分钟执行一次
//    @Scheduled(cron = "0 05 11 * * ?")//广告位出价到期时间是每天11点，每天11点零5分钟执行结算
    @Scheduled(cron = "0 0 0-23 * * ? ")//0-24小时，每小时整点开始执行

    public void checkAdvertisementExpire() {

        Calendar nowCalendar = Calendar.getInstance();
        //  nowCalendar.add(Calendar.HOUR, 1);//增加一小时，
        nowCalendar.set(Calendar.MINUTE, 0);
        nowCalendar.set(Calendar.SECOND, 0);
        nowCalendar.set(Calendar.MILLISECOND, 0);
        Date minStartDate = nowCalendar.getTime();
        logger.info("生成的开始时间:{}", minStartDate);
        String formatDate = DateUtils.formatDate(minStartDate, Constant.DATE_HOUR_FORMAT);
        // String formatDate = "2019-05-18-12";
        boolean hasMoreAdvertisement = false;
        String key = Constant.ADVERTISEMENT_EXPIRE_SET + formatDate;
        Long size = redisTemplate.opsForSet().size(key);
        if (size != null && size > 0) {
            hasMoreAdvertisement = true;
        }
        logger.info("checkAdvertisementExpire:定时检查过期广告位并结算,key:{},size:{}", key, size);


        while (hasMoreAdvertisement) {
            long count;
            if (size > QUERY_COUNT) {
                size = size - QUERY_COUNT;
                count = QUERY_COUNT;
            } else {
                count = size;
                hasMoreAdvertisement = false;
            }
            List<String> advertisementIdList = redisTemplate.opsForSet().pop(key, count);
            AdvertisementExample example = new AdvertisementExample();
            AdvertisementExample.Criteria criteria = example.createCriteria();
            criteria.andIdIn(advertisementIdList);
            List<Advertisement> advertisements = advertisementMapper.selectByExample(example);
            for (Advertisement advertisement : advertisements) {

                BigDecimal deposit = advertisement.getDeposit();
                String uId = advertisement.getuId();
                BigDecimal mysqlDeposit = null;
                if (deposit.compareTo(BigDecimal.ZERO) > 0) {

                    advertisement.setDeposit(BigDecimal.ZERO);
                    mysqlDeposit = userMapper.reduceDeposit(uId, deposit);
                    redisTemplate.opsForHash().put(Constant.USER_SESSION + uId, Constant.DEPOSIT, mysqlDeposit);
                    notifySyncUserInfo(uId);
                }

                advertisement.setStatus(Constant.ADVERTISEMENT_STATUS_SETTLEMENT);
                advertisement.setUpdated(new Date());
                advertisementMapper.updateByPrimaryKeySelective(advertisement);

                if (mysqlDeposit != null) {
                    logger.info("checkAdvertisementExpire:广告位到期结算，保证金减少,uId:{},advertisementId:{}, amount:{},用户数据库保证金deposit:{}", uId, advertisement.getId(), deposit, mysqlDeposit);
                } else {
                    logger.info("checkAdvertisementExpire:广告位到期结算，保证金为0，无须结算，uId:{},advertisementId:{},amount:{}", uId, advertisement.getId(), deposit);
                }
            }
        }
        updateAdvertisement(minStartDate);
    }

    private void notifySyncUserInfo(String uId) {
        JSONObject userPushInfo = new JSONObject();
        userPushInfo.put("uId", uId);
        redisTemplate.convertAndSend(Constant.NOTIFY_SYNC_USER_INFO, userPushInfo);
    }


    /**
     * 定时检查直播间下面广告位的最高价，生成订单
     */

    public void updateAdvertisement(Date minStartDate) {

        logger.info("updateAdvertisement:定时任务生成广告位订单");
        boolean hasNextPage = true;
        int startRow = 0;
        int pageSize = 100;

        while (hasNextPage) {

            List<LiveRoom> liveRooms = liveRoomMapper.selectByLimit(startRow, pageSize);
            if (liveRooms == null || liveRooms.size() == 0) {
                return;
            }
            if (liveRooms.size() < pageSize) {
                hasNextPage = false;
            }
            startRow += liveRooms.size();
            for (LiveRoom liveRoom : liveRooms) {
                String liveRoomId = liveRoom.getId();
                //根据当前时间点的小时部分，生成key
                String currentTimeMaxPriceKey = DateUtils.formatDate(minStartDate, Constant.DATE_HOUR_FORMAT);
                Object redisMaxPrice = redisTemplate.opsForHash().get(Constant.MAX_PRICE_RECORD + liveRoomId, currentTimeMaxPriceKey);
                if (redisMaxPrice != null) {
                    logger.info("updateAdvertisement: 直播间：{}，redis中记录的最高价:{},key:{}", liveRoomId, redisMaxPrice, currentTimeMaxPriceKey);
                    //必须做清除处理，不然过期的最高价堆积在redis 是无用的，redis中每个直播间下面， 最多保存未来24个小时时间段的最高价
                    redisTemplate.opsForHash().delete(Constant.MAX_PRICE_RECORD + liveRoomId, currentTimeMaxPriceKey);
                }
                //获取明天这个时段的最高价，缓存进redis
                Date addOneDay = DateUtils.addDays(minStartDate, 1);
                Advertisement tomorrowMaxPriceAdvertisement = advertisementMapper.selectMaxAmountAdvertisement(liveRoomId, addOneDay, DateUtils.addHour(addOneDay, 1));
                String tomorrowMaxPriceKey = DateUtils.formatDate(addOneDay, Constant.DATE_HOUR_FORMAT);

                if (tomorrowMaxPriceAdvertisement != null) {
                    advertisementDefaultPrice = tomorrowMaxPriceAdvertisement.getAmount();
                }
                redisTemplate.opsForHash().put(Constant.MAX_PRICE_RECORD + liveRoomId, tomorrowMaxPriceKey, advertisementDefaultPrice);

                Date endDate = DateUtils.addHour(minStartDate, 1);
                Advertisement advertisement = advertisementMapper.selectMaxAmountAdvertisement(liveRoomId, minStartDate, endDate);
                BigDecimal amount = null;
                //如果该直播间在当前时间段没有广告位投放，那应该去清理该直播间的广告位
                if (advertisement == null) {
                    //如果清除成功，说明直播间有广告位，应该通知app 端，清除广告位
                    Long delete = redisTemplate.opsForHash().delete(Constant.LIVE_ROOM + liveRoomId, "advertisement");
                    if (delete != 0) {
                        JSONObject info = new JSONObject();
                        info.put("liveRoomId", liveRoomId);
                        redisTemplate.convertAndSend(Constant.NOTIFY_ADVERTISEMENT_CHANGE, info);//通知直播间，广告位改变
                    }

                } else {
                    amount = advertisement.getAmount();
                }

                if (advertisement == null) {
                    logger.info("updateAdvertisement:直播间：{} 当前时段没有广告位投放， currentTimeMaxPriceKey:{}", liveRoomId, currentTimeMaxPriceKey);
                    continue;
                }
                //1.获取事务定义
                DefaultTransactionDefinition def = new DefaultTransactionDefinition();
                //2.设置事务隔离级别，开启新事务
                def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
                //3.获得事务状态
                TransactionStatus transactionStatus = transactionManager.getTransaction(def);
                Orders orders = new Orders();
                orders.setId(IDUtils.genOrderItemId());
                orders.setaId(advertisement.getId());
                String uId = advertisement.getuId();
                orders.setuId(uId);
                orders.setAmount(amount);
                orders.setStatus(Constant.ORDER_STATUS_TRADE_SUCCESS);
                orders.setType(Constant.ORDER_TYPE_TO_ADVERTISEMENT);
                orders.setCreated(minStartDate);//创建时间是广告位开始时间
                orders.setExpiryDate(endDate);//过期时间是广告位结束时间

                //支付订单
                // User user = payOffer_v1(orders, liveRoomId);

                User user;
                BigDecimal mysqlDonationAmount = null;
                Topic topic = null;
                if (amount.compareTo(new BigDecimal("1")) >= 0) {//广告竞价金额大于给定的阈值，才生成捐赠
                    BigDecimal donationAmount = amount.multiply(new BigDecimal(0.2)).setScale(2, RoundingMode.DOWN);

                    Donation historyDonation = donationMapper.selectMyValidHistoryDonation(uId, liveRoomId, Constant.DONATION_TYPE_ADVERTISEMENT);
                    String donationId;
                    if (historyDonation == null) {
                        donationId = IDUtils.genOrderItemId();
                        Donation donation = new Donation();
                        donation.setId(donationId);
                        donation.setmId(uId);
                        donation.setoId(liveRoomId);
                        donation.setAmount(donationAmount);
                        donation.setType(Constant.DONATION_TYPE_ADVERTISEMENT);
                        donation.setStatus(Constant.DONATION_STATUS_COULD_NOT_REVOKE);//广告位自动生成的捐赠不能拆回
                        donation.setCreated(minStartDate);
                        donationMapper.insertSelective(donation);
                        //增加主题收到的捐赠总额 和捐赠笔数
                        topic = topicMapper.incrementReceivedDonationData_v1(donationAmount, liveRoomId, minStartDate);
                    } else {
                        donationId = historyDonation.getId();
                        mysqlDonationAmount = donationMapper.incrementDonationAmount(donationId, donationAmount, minStartDate);
                        //增加主题收到的捐赠总额
                        topic = topicMapper.incrementReceivedDonationData(donationAmount, liveRoomId, minStartDate);
                    }
                    orders.setDonationId(donationId);
                    orders.setDonationAmount(donationAmount);
                    user = userMapper.payOfferV1(uId, amount, donationAmount, minStartDate);
                } else {
                    user = userMapper.pay(uId, amount);
                }
                ordersMapper.insertSelective(orders);
                //减少广告位的保证金
                BigDecimal advertisementDeposit = advertisementMapper.reduceDeposit(advertisement.getId(), amount, minStartDate);

                if (user.getDonationAmount() != null) {
                    //形成了捐赠的日志，
                    logger.info("updateAdvertisement:广告位竞价成功，支付订单，余额减少，保证金减少，捐赠金额增加 uId:{},amount:{},balance:{},deposit:{},个人捐赠总额 donationAmount:{}, advertisementId:{},ordersId:{}",
                            uId, amount, user.getBalance(), user.getDeposit(), user.getDonationAmount(), advertisement.getId(), orders.getId());
                    if (mysqlDonationAmount == null) {
                        logger.info("updateAdvertisement:广告位竞价成功，支付订单，超过捐赠阈值，生成新捐赠， uId:{}, advertisementId:{},ordersId:{},捐赠金额 :{},donationId:{},liveRoomId:{}",
                                uId, advertisement.getId(), orders.getId(), orders.getDonationAmount(), orders.getDonationId(), liveRoomId);
                    } else {
                        logger.info("updateAdvertisement:广告位竞价成功，支付订单，超过捐赠阈值，追加捐赠， uId:{}, advertisementId:{},ordersId:{},追加捐赠金额 :{},追加后总金额：{}, donationId:{},liveRoomId:{}",
                                uId, advertisement.getId(), orders.getId(), orders.getDonationAmount(), mysqlDonationAmount, orders.getDonationId(), liveRoomId);
                    }
                    if (topic != null) {
                        logger.info("updateAdvertisement:广告位竞价成功，支付订单，主题收到捐赠， uId:{},topicId:{}, ordersId:{},, donationId:{},捐赠金额：{},主题捐赠余额：{}，主题收到的捐赠总额：{},捐赠笔数：{}",
                                uId, liveRoomId, orders.getId(), orders.getDonationId(), orders.getDonationAmount(), topic.getDonationBalance(), topic.getDonationReceived(), topic.getDonationSize());
                    }
                } else {
                    //没有捐赠，只有保证金变化的 个人账户变动日志
                    logger.info("updateAdvertisement:广告位竞价成功，支付订单，余额减少，保证金减少 uId:{}, amount:{},balance:{},deposit:{},advertisementId:{},ordersId:{}",
                            uId, amount, user.getBalance(), user.getDeposit(), advertisement.getId(), orders.getId());
                }

                logger.info("updateAdvertisement:广告位竞价成功，支付订单，广告位保证金减少  uId:{},advertisementId:{},ordersId:{},广告位剩余保证金:{}",
                        uId, advertisement.getId(), orders.getId(), advertisementDeposit);


                liveRoom.setaId(advertisement.getId());
                liveRoom.setUpdated(minStartDate);
                liveRoomMapper.updateByPrimaryKeySelective(liveRoom);
                redisTemplate.opsForHash().put(Constant.LIVE_ROOM + liveRoom.getId(), "advertisement", advertisement);//重置liveRoom缓存中的广告id

                //事务提交
                transactionManager.commit(transactionStatus);

                //有用户缓存，我们认为用户是登录状态，就更新缓存中用户的信息
                if (redisTemplate.hasKey(Constant.USER_SESSION + uId)) {
                    redisTemplate.opsForHash().putAll(Constant.USER_SESSION + uId, EntityUtils.objectToMap_v1(user));
                }

                //判断竞价成功的广告位是否设置了 广告展示的内容，如果视频地址和图片地址都为空，说明没有设置
                if (advertisement.getVideo() == null && advertisement.getPicture() == null) {

                    //通知用户竞价成功，同步个人信息，并去设置广告展示内容
                    JSONObject userPushInfo = new JSONObject();
                    userPushInfo.put("uId", uId);
                    userPushInfo.put("advertisement", advertisement);
                    redisTemplate.convertAndSend(Constant.NOTIFY_ADVERTISEMENT_NEED_SET_SHOW_CONTENT, userPushInfo);
                } else {
                    //通知用户竞价成功，同步个人信息，
                    notifySyncUserInfo(uId);
                    //通知直播间，广告位改变
                    JSONObject info = new JSONObject();
                    info.put("liveRoomId", liveRoomId);
                    info.put("advertisement", advertisement);
                    redisTemplate.convertAndSend(Constant.NOTIFY_ADVERTISEMENT_CHANGE, info);
                }


            }
        }
    }


}
